home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / ip / trace / tcpdump-2.2.1 / tcpslice.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-02  |  15.2 KB  |  646 lines

  1. /*
  2.  * Copyright (c) 1987-1990 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that: (1) source code distributions
  7.  * retain the above copyright notice and this paragraph in its entirety, (2)
  8.  * distributions including binary code include the above copyright notice and
  9.  * this paragraph in its entirety in the documentation or other materials
  10.  * provided with the distribution, and (3) all advertising materials mentioning
  11.  * features or use of this software display the following acknowledgement:
  12.  * ``This product includes software developed by the University of California,
  13.  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
  14.  * the University nor the names of its contributors may be used to endorse
  15.  * or promote products derived from this software without specific prior
  16.  * written permission.
  17.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  18.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  19.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  20.  */
  21. #ifndef lint
  22. char copyright[] =
  23.     "@(#) Copyright (c) 1987-1990 The Regents of the University of California.\nAll rights reserved.\n";
  24. static  char rcsid[] =
  25.     "@(#)$Header: tcpslice.c,v 1.10 92/06/02 17:57:44 mccanne Exp $ (LBL)";
  26. #endif
  27.  
  28. /*
  29.  * tcpslice - extract pieces of and/or glue together tcpdump files
  30.  */
  31.  
  32. #include <stdio.h>
  33. #include <ctype.h>
  34. #include <string.h>
  35. #include <sys/types.h>
  36. #include <sys/time.h>
  37. #include <sys/timeb.h>
  38. #include <netinet/in.h>
  39. #include <varargs.h>
  40.  
  41. #include "savefile.h"
  42. #include "version.h"
  43.  
  44.  
  45. int tflag = 0;    /* global that util routines are sensitive to */
  46.  
  47. char *program_name;
  48.  
  49. long thiszone;            /* gmt to local correction in trace file */
  50.  
  51. /* Length of saved portion of packet. */
  52. int snaplen;
  53.  
  54. /* Length of saved portion of data past link level protocol.  */
  55. int snapdlen;
  56.  
  57. /* Precision of clock used to generate trace file. */
  58. int precision;
  59.  
  60. static int linkinfo;
  61.  
  62. /* Style in which to print timestamps; RAW is "secs.usecs"; READABLE is
  63.  * ala the Unix "date" tool; and PARSEABLE is tcpslice's custom format,
  64.  * designed to be easy to parse.  The default is RAW.
  65.  */
  66. enum stamp_styles { TIMESTAMP_RAW, TIMESTAMP_READABLE, TIMESTAMP_PARSEABLE };
  67. enum stamp_styles timestamp_style = TIMESTAMP_RAW;
  68.  
  69.  
  70. time_t gwtm2secs( /* struct tm *tmp */ );
  71.  
  72.  
  73. long local_time_zone( /* timestamp */ );
  74. struct timeval parse_time(/* time_string, base_time*/);
  75. void fill_tm(/* time_string, is_delta, t, usecs_addr */);
  76. void get_file_range( /* filename, first_time, last_time */ );
  77. struct timeval first_packet_time(/* filename */);
  78. void extract_slice(/* filename, start_time, stop_time */);
  79. char *timestamp_to_string( /* timestamp */ );
  80. void dump_times(/* filename */);
  81. void usage();
  82.  
  83.  
  84. int
  85. main(argc, argv)
  86.     int argc;
  87.     char **argv;
  88. {
  89.     int op;
  90.     int dump_flag = 0;
  91.     int report_times = 0;
  92.     char *start_time_string = 0;
  93.     char *stop_time_string = 0;
  94.     char *write_file_name = "-";    /* default is stdout */
  95.     struct timeval first_time, start_time, stop_time;
  96.  
  97.     extern char *optarg;
  98.     extern int optind, opterr;
  99.  
  100.     program_name = argv[0];
  101.  
  102.     opterr = 0;
  103.     while ((op = getopt(argc, argv, "dRrtw:")) != EOF)
  104.         switch (op) {
  105.  
  106.         case 'd':
  107.             dump_flag = 1;
  108.             break;
  109.  
  110.         case 'R':
  111.             ++report_times;
  112.             timestamp_style = TIMESTAMP_RAW;
  113.             break;
  114.  
  115.         case 'r':
  116.             ++report_times;
  117.             timestamp_style = TIMESTAMP_READABLE;
  118.             break;
  119.  
  120.         case 't':
  121.             ++report_times;
  122.             timestamp_style = TIMESTAMP_PARSEABLE;
  123.             break;
  124.  
  125.         case 'w':
  126.              write_file_name = optarg;
  127.              break;
  128.  
  129.         default:
  130.             usage();
  131.             /* NOTREACHED */
  132.         }
  133.  
  134.     if ( report_times > 1 )
  135.         error( "only one of -R, -r, or -t can be specified" );
  136.  
  137.  
  138.     if (optind < argc)
  139.         /* See if the next argument looks like a possible
  140.          * start time, and if so assume it is one.
  141.          */
  142.         if (isdigit(argv[optind][0]) || argv[optind][0] == '+')
  143.             start_time_string = argv[optind++];
  144.  
  145.     if (optind < argc)
  146.         if (isdigit(argv[optind][0]) || argv[optind][0] == '+')
  147.             stop_time_string = argv[optind++];
  148.  
  149.  
  150.     if (optind >= argc)
  151.         error("at least one input file must be given");
  152.  
  153.  
  154.     first_time = first_packet_time(argv[optind]);
  155.     fclose( sf_readfile );
  156.  
  157.  
  158.     if (start_time_string)
  159.         start_time = parse_time(start_time_string, first_time);
  160.     else
  161.         start_time = first_time;
  162.  
  163.     if (stop_time_string)
  164.         stop_time = parse_time(stop_time_string, start_time);
  165.  
  166.     else
  167.         {
  168.         stop_time = start_time;
  169.         stop_time.tv_sec += 86400*3660;    /* + 10 years; "forever" */
  170.         }
  171.  
  172.  
  173.     if (report_times) {
  174.         for (; optind < argc; ++optind)
  175.             dump_times(argv[optind]);
  176.     }
  177.     
  178.     if (dump_flag) {
  179.         printf( "start\t%s\nstop\t%s\n",
  180.             timestamp_to_string( &start_time ),
  181.             timestamp_to_string( &stop_time ) );
  182.     }
  183.  
  184.     if (! report_times && ! dump_flag) {
  185.         if ( ! strcmp( write_file_name, "-" ) &&
  186.              isatty( fileno(stdout) ) )
  187.             error("stdout is a terminal; redirect or use -w");
  188.  
  189.         sf_write_init(write_file_name, linkinfo, thiszone, snaplen,
  190.             precision);
  191.  
  192.         for (; optind < argc; ++optind)
  193.             extract_slice(argv[optind], &start_time, &stop_time);
  194.  
  195.         fclose( sf_writefile );
  196.     }
  197.  
  198.     return 0;
  199. }
  200.  
  201.  
  202. /* Returns non-zero if a string matches the format for a timestamp,
  203.  * 0 otherwise.
  204.  */
  205. int is_timestamp( str )
  206. char *str;
  207.     {
  208.     while ( isdigit(*str) || *str == '.' )
  209.         ++str;
  210.  
  211.     return *str == '\0';
  212.     }
  213.  
  214.  
  215. /* Return the correction in seconds for the local time zone with respect
  216.  * to Greenwich time.
  217.  */
  218. long local_time_zone(timestamp)
  219. long timestamp;
  220. {
  221.     struct timeval now;
  222.     struct timezone tz;
  223.     long localzone;
  224.  
  225.     if (gettimeofday(&now, &tz) < 0) {
  226.         perror("tcpslice: gettimeofday");
  227.         exit(1);
  228.     }
  229.     localzone = tz.tz_minuteswest * -60;
  230.  
  231.     if (localtime((time_t *) ×tamp)->tm_isdst)
  232.         localzone += 3600;
  233.  
  234.     return localzone;
  235. }
  236.  
  237. /* Given a string specifying a time (or a time offset) and a "base time"
  238.  * from which to compute offsets and fill in defaults, returns a timeval
  239.  * containing the specified time.
  240.  */
  241.  
  242. struct timeval
  243. parse_time(time_string, base_time)
  244.     char *time_string;
  245.     struct timeval base_time;
  246. {
  247.     struct tm *bt = localtime((time_t *) &base_time.tv_sec);
  248.     struct tm t;
  249.     struct timeval result;
  250.     time_t usecs = 0;
  251.     int is_delta = (time_string[0] == '+');
  252.  
  253.     if ( is_delta )
  254.         ++time_string;    /* skip over '+' sign */
  255.  
  256.     if ( is_timestamp( time_string ) )
  257.         { /* interpret as a raw timestamp or timestamp offset */
  258.         char *time_ptr;
  259.  
  260.         result.tv_sec = atoi( time_string );
  261.         time_ptr = strchr( time_string, '.' );
  262.  
  263.         if ( time_ptr )
  264.             { /* microseconds are specified, too */
  265.             int num_digits = strlen( time_ptr + 1 );
  266.             result.tv_usec = atoi( time_ptr + 1 );
  267.  
  268.             /* turn 123.456 into 123 seconds plus 456000 usec */
  269.             while ( num_digits++ < 6 )
  270.                 result.tv_usec *= 10;
  271.             }
  272.  
  273.         else
  274.             result.tv_usec = 0;
  275.  
  276.         if ( is_delta )
  277.             {
  278.             result.tv_sec += base_time.tv_sec;
  279.             result.tv_usec += base_time.tv_usec;
  280.  
  281.             if ( result.tv_usec > 1000000 )
  282.                 {
  283.                 result.tv_usec -= 1000000;
  284.                 ++result.tv_sec;
  285.                 }
  286.             }
  287.  
  288.         return result;
  289.         }
  290.  
  291.     if (is_delta) {
  292.         t = *bt;
  293.         usecs = base_time.tv_usec;
  294.     } else {
  295.         /* Zero struct (easy way around lack of tm_gmtoff/tm_zone
  296.          * under older systems) */
  297.         bzero((char *)&t, sizeof(t));
  298.  
  299.         /* Set values to "not set" flag so we can later identify
  300.          * and default them.
  301.          */
  302.         t.tm_sec = t.tm_min = t.tm_hour = t.tm_mday = t.tm_mon =
  303.             t.tm_year = -1;
  304.     }
  305.  
  306.     fill_tm(time_string, is_delta, &t, &usecs);
  307.  
  308.     /* Now until we reach a field that was specified, fill in the
  309.      * missing fields from the base time.
  310.      */
  311. #define CHECK_FIELD(field_name)         \
  312.     if (t.field_name < 0)             \
  313.         t.field_name = bt->field_name;    \
  314.     else                    \
  315.         break
  316.  
  317.     do {    /* bogus do-while loop so "break" in CHECK_FIELD will work */
  318.         CHECK_FIELD(tm_year);
  319.         CHECK_FIELD(tm_mon);
  320.         CHECK_FIELD(tm_mday);
  321.         CHECK_FIELD(tm_hour);
  322.         CHECK_FIELD(tm_min);
  323.         CHECK_FIELD(tm_sec);
  324.     } while ( 0 );
  325.  
  326.     /* Set remaining unspecified fields to 0. */
  327. #define ZERO_FIELD_IF_NOT_SET(field_name,zero_val)    \
  328.     if (t.field_name < 0)                \
  329.         t.field_name = zero_val
  330.  
  331.     if (! is_delta) {
  332.         ZERO_FIELD_IF_NOT_SET(tm_year,90);  /* should never happen */
  333.         ZERO_FIELD_IF_NOT_SET(tm_mon,0);
  334.         ZERO_FIELD_IF_NOT_SET(tm_mday,1);
  335.         ZERO_FIELD_IF_NOT_SET(tm_hour,0);
  336.         ZERO_FIELD_IF_NOT_SET(tm_min,0);
  337.         ZERO_FIELD_IF_NOT_SET(tm_sec,0);
  338.     }
  339.  
  340.     result.tv_sec = gwtm2secs(&t);
  341.     result.tv_sec -= local_time_zone(result.tv_sec);
  342.     result.tv_usec = usecs;
  343.  
  344.     return result;
  345. }
  346.  
  347.  
  348. /* Fill in (or add to, if is_delta is true) the time values in the
  349.  * tm struct "t" as specified by the time specified in the string
  350.  * "time_string".  "usecs_addr" is updated with the specified number
  351.  * of microseconds, if any.
  352.  */
  353. void
  354. fill_tm(time_string, is_delta, t, usecs_addr)
  355.     char *time_string;
  356.     int is_delta;    /* if true, add times in instead of replacing */
  357.     struct tm *t;    /* tm struct to be filled from time_string */
  358.     time_t *usecs_addr;
  359. {
  360.     char *t_start, *t_stop, format_ch;
  361.     int val;
  362.  
  363. #define SET_VAL(lhs,rhs)    \
  364.     if (is_delta)        \
  365.         lhs += rhs;    \
  366.     else            \
  367.         lhs = rhs
  368.  
  369.     /* Loop through the time string parsing one specification at
  370.      * a time.  Each specification has the form <number><letter>
  371.      * where <number> indicates the amount of time and <letter>
  372.      * the units.
  373.      */
  374.     for (t_stop = t_start = time_string; *t_start; t_start = ++t_stop) {
  375.         if (! isdigit(*t_start))
  376.             error("bad date format %s, problem starting at %s",
  377.                   time_string, t_start);
  378.  
  379.         while (isdigit(*t_stop))
  380.             ++t_stop;
  381.         if (! t_stop)
  382.             error("bad date format %s, problem starting at %s",
  383.                   time_string, t_start);
  384.  
  385.         val = atoi(t_start);
  386.  
  387.         format_ch = *t_stop;
  388.         if ( isupper( format_ch ) )
  389.             format_ch = tolower( format_ch );
  390.  
  391.         switch (format_ch) {
  392.             case 'y':
  393.                 if ( val > 1900 )
  394.                     val -= 1900;
  395.                 SET_VAL(t->tm_year, val);
  396.                 break;
  397.  
  398.             case 'm':
  399.                 if (strchr(t_stop+1, 'D') ||
  400.                     strchr(t_stop+1, 'd'))
  401.                     /* it's months */
  402.                     SET_VAL(t->tm_mon, val - 1);
  403.                 else    /* it's minutes */
  404.                     SET_VAL(t->tm_min, val);
  405.                 break;
  406.  
  407.             case 'd':
  408.                 SET_VAL(t->tm_mday, val);
  409.                 break;
  410.  
  411.             case 'h':
  412.                 SET_VAL(t->tm_hour, val);
  413.                 break;
  414.  
  415.             case 's':
  416.                 SET_VAL(t->tm_sec, val);
  417.                 break;
  418.  
  419.             case 'u':
  420.                 SET_VAL(*usecs_addr, val);
  421.                 break;
  422.  
  423.             default:
  424.                 error(
  425.                 "bad date format %s, problem starting at %s",
  426.                       time_string, t_start);
  427.         }
  428.     }
  429. }
  430.  
  431.  
  432. /* Return in first_time and last_time the timestamps of the first and
  433.  * last packets in the given file.
  434.  */
  435. void
  436. get_file_range( filename, first_time, last_time )
  437.     char filename[];
  438.     struct timeval *first_time;
  439.     struct timeval *last_time;
  440. {
  441.     *first_time = first_packet_time( filename );
  442.  
  443.     if ( ! sf_find_end( first_time, last_time ) )
  444.         error( "couldn't find final packet in file %s", filename );
  445. }
  446.  
  447.  
  448. /* Returns the timestamp of the first packet in the given tcpdump save
  449.  * file, which as a side-effect is initialized for further save-file
  450.  * reading.
  451.  */
  452.  
  453. struct timeval
  454. first_packet_time(filename)
  455.     char filename[];
  456. {
  457.     struct packet_header hdr;
  458.     u_char *buf;
  459.  
  460.     if (sf_read_init(filename, &linkinfo, &thiszone, &snaplen, &precision))
  461.         error( "bad tcpdump file %s", filename );
  462.  
  463.     buf = (u_char *)malloc((unsigned)snaplen);
  464.  
  465.     if (sf_next_packet(&hdr, buf, snaplen))
  466.         error( "bad status reading first packet in %s", filename );
  467.  
  468.     free((char *)buf);
  469.  
  470.     return hdr.ts;
  471. }
  472.  
  473.  
  474. /* Extract from the given file all packets with timestamps between
  475.  * the two time values given (inclusive).  These packets are written
  476.  * to the save file output set up by a previous call to sf_write_init().
  477.  * Upon return, start_time is adjusted to reflect a time just after
  478.  * that of the last packet written to the output.
  479.  */
  480.  
  481. void
  482. extract_slice(filename, start_time, stop_time)
  483.     char filename[];
  484.     struct timeval *start_time;
  485.     struct timeval *stop_time;
  486. {
  487.     long start_pos, stop_pos;
  488.     struct timeval file_start_time, file_stop_time;
  489.     int status;
  490.     struct packet_header hdr;
  491.     u_char *buf;
  492.  
  493.  
  494.     if (sf_read_init(filename, &linkinfo, &thiszone, &snaplen, &precision))
  495.         error( "bad tcpdump file %s", filename );
  496.  
  497.     buf = (u_char *)malloc((unsigned)snaplen);
  498.  
  499.     start_pos = ftell( sf_readfile );
  500.  
  501.  
  502.     if ( (status = sf_next_packet( &hdr, buf, snaplen )) )
  503.         error( "bad status %d reading packet in %s",
  504.             status, filename );
  505.  
  506.     file_start_time = hdr.ts;
  507.  
  508.  
  509.     if ( ! sf_find_end( &file_start_time, &file_stop_time ) )
  510.         error( "problems finding end packet of file %s",
  511.             filename );
  512.  
  513.     stop_pos = ftell( sf_readfile );
  514.  
  515.  
  516.     /* sf_find_packet() requires that the time it's passed as its last
  517.      * argument be in the range [min_time, max_time], so we enforce
  518.      * that constraint here.
  519.      */
  520.     if ( sf_timestamp_less_than( start_time, &file_start_time ) )
  521.         *start_time = file_start_time;
  522.  
  523.     if ( sf_timestamp_less_than( &file_stop_time, start_time ) )
  524.         return;    /* there aren't any packets of interest in the file */
  525.  
  526.  
  527.     sf_find_packet( &file_start_time, start_pos,
  528.             &file_stop_time, stop_pos,
  529.             start_time );
  530.  
  531.     for ( ; ; )
  532.         {
  533.         struct timeval *timestamp;
  534.         status = sf_next_packet( &hdr, buf, snaplen );
  535.  
  536.         if ( status )
  537.             {
  538.             if ( status != SFERR_EOF )
  539.                 error( "bad status %d reading packet in %s",
  540.                     status, filename );
  541.             break;
  542.             }
  543.  
  544.         timestamp = &hdr.ts;
  545.  
  546.         if ( ! sf_timestamp_less_than( timestamp, start_time ) )
  547.             { /* packet is recent enough */
  548.             if ( sf_timestamp_less_than( stop_time, timestamp ) )
  549.                 /* We've gone beyond the end of the region
  550.                  * of interest ... We're done with this file.
  551.                  */
  552.                 break;
  553.  
  554.             sf_write( buf, timestamp, (int) hdr.len,
  555.                   (int) hdr.caplen );
  556.             *start_time = *timestamp;
  557.  
  558.             /* We know that each packet is guaranteed to have
  559.              * a unique timestamp, so we push forward the
  560.              * allowed minimum time to weed out duplicate
  561.              * packets.
  562.              */
  563.             ++start_time->tv_usec;
  564.             }
  565.         }
  566.  
  567.     fclose( sf_readfile );
  568.     free( (char *) buf );
  569. }
  570.  
  571.  
  572. /* Translates a timestamp to the time format specified by the user.
  573.  * Returns a pointer to the translation residing in a static buffer.
  574.  * There are two such buffers, which are alternated on subseqeuent
  575.  * calls, so two calls may be made to this routine without worrying
  576.  * about the results of the first call being overwritten by the
  577.  * results of the second.
  578.  */
  579.  
  580. char *
  581. timestamp_to_string(timestamp)
  582.     struct timeval *timestamp;
  583. {
  584.     struct tm *t;
  585. #define NUM_BUFFERS 2
  586.     static char buffers[NUM_BUFFERS][128];
  587.     static int buffer_to_use = 0;
  588.     char *buf;
  589.  
  590.     buf = buffers[buffer_to_use];
  591.     buffer_to_use = (buffer_to_use + 1) % NUM_BUFFERS;
  592.  
  593.     switch ( timestamp_style )
  594.         {
  595.         case TIMESTAMP_RAW:
  596.         sprintf( buf, "%d.%d", timestamp->tv_sec, timestamp->tv_usec );
  597.         break;
  598.  
  599.         case TIMESTAMP_READABLE:
  600.         t = localtime((time_t *) ×tamp->tv_sec);
  601.         strcpy( buf, asctime( t ) );
  602.         buf[24] = '\0';    /* nuke final newline */
  603.         break;
  604.  
  605.         case TIMESTAMP_PARSEABLE:
  606.         t = localtime((time_t *) ×tamp->tv_sec);
  607.         sprintf( buf, "%02dy%02dm%02dd%02dh%02dm%02ds%06du",
  608.             t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour,
  609.             t->tm_min, t->tm_sec, timestamp->tv_usec );
  610.         break;
  611.  
  612.         }
  613.  
  614.     return buf;
  615. }
  616.  
  617.  
  618. /* Given a tcpdump save filename, reports on the times of the first
  619.  * and last packets in the file.
  620.  */
  621.  
  622. void
  623. dump_times(filename)
  624.     char filename[];
  625. {
  626.     struct timeval first_time, last_time;
  627.  
  628.     get_file_range( filename, &first_time, &last_time );
  629.  
  630.     printf( "%s\t%s\t%s\n",
  631.         filename,
  632.         timestamp_to_string( &first_time ),
  633.         timestamp_to_string( &last_time ) );
  634. }
  635.  
  636. void
  637. usage()
  638. {
  639.     (void)fprintf(stderr, "tcpslice for tcpdump version %d.%d\n",
  640.               VERSION_MAJOR, VERSION_MINOR);
  641.     (void)fprintf(stderr,
  642. "Usage: tcpslice [-dRrt] [-w file] [start-time [end-time]] file ... \n");
  643.  
  644.     exit(-1);
  645. }
  646.